import levelList from 'province-city-china/dist/level.json'
import provinceList from 'province-city-china/dist/province.json'
import cityList from 'province-city-china/dist/city.json'
import { saveAs } from 'file-saver'

const codeToFullPath = new Map()

const buildCodePathMap = (dataList = levelList, names = [], codes = []) => {
  for (let node of dataList) {
    const namePath = [...names, node.name]
    const codePath = [...codes, node.code]
    codeToFullPath.set(node.code, {
      nameArr: namePath,
      codeArr: codePath
    })
    codeToFullPath.set(JSON.stringify(codePath), {
      nameArr: namePath
    })
    if (node.children) buildCodePathMap(node.children, namePath, codePath)
  }
}

buildCodePathMap()

export { codeToFullPath }

/**
 * 判断是否是外部链接
 *
 * @param {string} path
 * @returns {Boolean}
 */
export const isExternal = path => {
  return /^(https?:|http?:|mailto:|tel:)/.test(path)
}

/**
 * @param {string} str
 * @returns {Boolean}
 */
export function validUsername(str) {
  const valid_map = ['admin', 'editor']
  return valid_map.indexOf(str.trim()) >= 0
}

/**
 * @param {string} url
 * @returns {Object}
 */
export function param2Obj(url) {
  try {
    const search = new URLSearchParams(url.split('?')[1])
    const result = {}
    for (const [key, value] of search) {
      result[key] = value
    }
    return result
  } catch {
    return {}
  }
}
export function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result

  const later = function () {
    // 据上一次触发时间间隔
    const last = +new Date() - timestamp

    // 上次被包装函数被调用时间间隔last小于设定时间间隔wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      // 如果设定为immediate===true，因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }

  return function (...args) {
    context = this
    timestamp = +new Date()
    const callNow = immediate && !timeout
    // 如果延时不存在，重新设定延时
    if (!timeout) timeout = setTimeout(later, wait)
    if (callNow) {
      result = func.apply(context, args)
      context = args = null
    }

    return result
  }
}

// translate router.meta.title 用于breadcrumb侧边栏标签视图
export const generateTitle = title => {
  return g_i18n.global.te('route.' + title) ? g_t('route.' + title) : title
}

/**
 * @description 根据数值去数字和单位显示
 * @param {} value
 * @returns string
 */
export const numberConversion = value => {
  const units = [
    { value: 10 ** 4, unit: g_t('public.million') },
    { value: 10 ** 7, unit: g_t('public.tenMillion') },
    { value: 10 ** 8, unit: g_t('public.Billions') },
    { value: 10 ** 11, unit: g_t('public.hundredsOfBillions') },
    { value: 10 ** 12, unit: g_t('public.trillions') },
    { value: 10 ** 15, unit: g_t('public.thousandTrillions') }
  ]
  let unitIndex = 0
  while (value >= units[unitIndex].value && unitIndex < units.length - 1) {
    unitIndex++
  }
  const val = !unitIndex ? value : value / units[unitIndex - 1].value
  const unit = !unitIndex ? '' : units[unitIndex - 1].unit
  return val + unit
}

/**
 * @description 转化字节数据
 * @param {*} data 表示要被转化的容量大小，以字节为单位
 * @param {*} digit 表示如果转换时出小数，四舍五入保留多少位 默认为2位小数
 * @returns
 */
export const transformByteData = (data, digit = 2, extension = '') => {
  if (!data) return `0${extension}B`
  const c = 1024
  const e = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
  const i = Math.floor(Math.log(data) / Math.log(c))
  return (data / c ** i).toFixed(digit) + extension + e[i]
}

// 动态计算文本的宽度
export const calcTextWidth = (
  str = '',
  style = {
    fontSize: '16px',
    fontWeight: 'bold'
  }
) => {
  // 新建一个 span
  const span = document.createElement('span')
  // 设置表头名称
  span.innerText = str
  // 设置表头样式
  span.style.fontSize = style.fontSize
  span.style.fontWeight = style.fontWeight
  // 临时插入 document
  document.body.appendChild(span)
  // 重点：获取 span 最小宽度，设置当前列，注意这里加了 20，字段较多时还是有挤压，且渲染后的 div 内左右 padding 都是 10，所以 +20 。（可能还有边距/边框等值，需要根据实际情况加上）
  const width = span.getBoundingClientRect().width + 25
  // 移除 document 中临时的 span
  document.body.removeChild(span)
  return Math.ceil(width) // 存在宽度为小数的情况
}

// 复制文本
export const copyText = (textToCopy = '') => {
  // navigator clipboard 需要https等安全上下文
  if (navigator.clipboard && window.isSecureContext) {
    ElMessage.success(g_t('public.copySuccess'))
    // navigator clipboard 向剪贴板写文本
    return navigator.clipboard.writeText(textToCopy)
  } else {
    // 创建text area
    const textArea = document.createElement('textarea')
    textArea.value = textToCopy
    // 使text area不在viewport，同时设置不可见
    Object.assign(textArea.style, {
      position: 'fixed',
      opacity: '0',
      top: '-9999px',
      left: '-9999px'
    })
    document.body.appendChild(textArea)
    textArea.focus()
    textArea.select()
    return new Promise((res, rej) => {
      // 执行复制命令并移除文本框
      document.execCommand('copy') ? res() : rej()
      textArea.remove()
      ElMessage.success(g_t('public.copySuccess'))
    })
  }
}

/**
 * @description 根据当前页面路径查询当前页面的所有权限控制按钮
 * @param {*} permList
 * @param {*} path
 * @returns
 */
export const getPerm = (permList, path) => {
  let perm = []
  if (permList && permList.length > 0) {
    const obj = permList.find(item => path === item.menuUrl)
    obj && (perm = obj.permList)
  }
  return JSON.parse(JSON.stringify(perm))
}

/**
 * @description 在详情弹窗中，获取文本的最大宽度
 * @returns
 */
export const getTextMaxWidth = function () {
  const language = useOutsideAppStore().language
  nextTick(_ => {
    // 获取一系列文本的最大文本宽度
    document.querySelectorAll('.detail-dialog .el-card__body').forEach(ele => {
      let maxWidth = 0
      ele.querySelectorAll('.item-title').forEach(item => {
        if (item.offsetWidth > maxWidth) maxWidth = item.offsetWidth
      })
      ele.querySelectorAll('.item-title').forEach(item => {
        item.style['min-width'] = maxWidth + 'px'
        if (language === 'zh') {
          item.style['text-align'] = 'justify'
        } else {
          item.style['text-align'] = 'initial'
        }
      })
    })
  })
}

/**
 * 使用vite官方提供的方式，获取图片的路径
 *
 * @param {*} url
 * @returns {*}
 */
export const getViteImageUrl = url => {
  return new URL(`/src/assets/${url}`, import.meta.url).href
}

/**
 * 删操作前的确认框
 * @param {*} _
 * @returns
 */
export const deleteConfirm = (content = g_t('public.confirmDeleteSure')) => {
  return ElMessageBox.confirm(content, g_t('public.confirmDeleteTip'), {
    confirmButtonText: g_t('public.confirm'),
    cancelButtonText: g_t('public.cancel'),
    type: 'warning'
  })
}

/**
 * 显示文本的处理逻辑
 * @param {*} str
 * @returns
 */
export const formatValue = (str, defaultValue = '--') => {
  return str === 0 || str ? str : defaultValue
}

/**
 * 根据单个编码获取编码名称
 * @param {String||Array} regionCode
 * @returns
 */
export const getRegionName = (regionCode, separator = '/') => {
  regionCode = Array.isArray(regionCode)
    ? JSON.stringify(regionCode)
    : regionCode + ''
  return codeToFullPath.get(regionCode)?.nameArr.join(separator) || ''
}

/**
 * 根据单个编码获取全部层级编码
 * @param {String} regionCode
 * @returns
 */
export const getRegionCode = strCode => {
  return codeToFullPath.get(strCode + '')?.codeArr || []
}

/**
 * 根据省市文本获取区域代码,AI识别文本中使用
 * @param {String} name
 * @returns
 */
export const getRegionData = name => {
  const regionData = { codeArr: [], nameArr: [] }
  if (!name) return regionData
  const findRegion = (list, condition) => list.find(condition)
  const province = findRegion(
    provinceList,
    item => name.includes(item.name) || item.name.includes(name)
  )
  if (province) {
    regionData.codeArr.push(province.code)
    regionData.nameArr.push(province.name)
  }

  const city = findRegion(
    cityList,
    item =>
      province?.province === item.province &&
      (name.includes(item.name) || item.name.includes(name))
  )
  if (city) {
    regionData.codeArr.push(city.code)
    regionData.nameArr.push(city.name)
  }

  return regionData
}

/**
 * 赋值数据到表单数据中
 * @param {Object} formData
 * @param {Object} data
 */
export const assignFormData = (formData, data) => {
  Object.keys(formData).forEach(k => {
    if (g_.isArray(data[k]) || g_.isObject(data[k])) {
      formData[k] = data[k]
    } else if (data[k] || data[k] === 0) {
      formData[k] = data[k] + ''
    }
  })
}

/**
 * 根据地址信息获取经纬度（使用高德地图 Geocoder）
 * @param {string} address - 地址字符串，例如：'北京市朝阳区'
 * @returns {Promise<{lng: string, lat: string}|null>} - 返回包含经度和纬度的对象，失败时返回空值或 null
 */
export const getAMapLocation = address => {
  // 初始化 Geocoder
  const geocoder = new AMap.Geocoder({
    city: '全国' // 城市，默认："全国"
  })
  return new Promise(resolve => {
    geocoder.getLocation(address, (status, result) => {
      if (status === 'complete' && result.geocodes.length) {
        const { location } = result.geocodes[0]
        resolve({
          lng: location.lng,
          lat: location.lat
        })
      } else {
        console.warn(`地理编码失败: 无法解析地址 [${address}]`)
        resolve({
          lng: '',
          lat: ''
        })
      }
    })
  })
}

/**
 * 获取浮点数，默认保留小数点后两位
 * @param {*} num
 * @param {*} fixed
 * @returns
 */
export const getFloat = (num, fixed = 2) => {
  return (num * 1 || 0).toFixed(fixed)
}

/**
 * 获取当前用户的城市编码，默认在深圳市
 * @param {*} _
 */
export const getCurCityCode = _ => {
  return new Promise(resolve => {
    new AMap.CitySearch().getLocalCity((status, result) => {
      // console.log('status', status, 'result', result)
      let region = '440300'
      if (status === 'complete' && result.info === 'OK') {
        region = getRegionData(result.province + result.city)
        resolve(region.codeArr[region.codeArr.length - 1])
      }
    })
  })
}

/**
 * @description 文件下载
 * @param {*} fileUrl
 * @param {*} fileName
 */
export const downloadFile = (fileUrl, fileName) => {
  if (isExternal(fileUrl)) {
    g_https
      .get(
        fileUrl,
        {},
        {
          responseType: 'blob'
        },
        {
          showMessage: false
        }
      )
      .then(
        data => {
          saveAs(new Blob([data]), fileName)
        },
        err => {
          window.open(fileUrl)
        }
      )
  } else {
    // 提取 base64 部分
    const base64Data = fileUrl.split(',')[1]
    // 转换为 Blob
    const byteCharacters = atob(base64Data)
    const byteArrays = []
    for (let i = 0; i < byteCharacters.length; i++) {
      byteArrays.push(byteCharacters.charCodeAt(i))
    }
    const blob = new Blob([new Uint8Array(byteArrays)])
    // 保存文件
    saveAs(blob, fileName)
  }
}
